#include "head.cuh"

int main(void){
	printf("Now: %s\n", UIviewFileName);

	Params params;
	params.split_rate = 0.8f;

	float *UIview = NULL;
	float *IUview = NULL;
	float *UUview = NULL;
	float *UIview_test = NULL;
	std::stringstream ss;
	
	float* d_UIview = NULL;
	float* d_anchors_of_uiview = NULL;
	float *anchors_of_uiview = NULL;
	float *d_UUview = NULL;
	float *d_anchors_of_uuview = NULL;
	float *anchors_of_uuview = NULL;
	float *uZ = NULL;
	cublasHandle_t cublashandle;
	
	float *d_IUview = NULL;
	float *d_anchors_of_iuview = NULL;
	float *anchors_of_iuview = NULL;
	float *iZ = NULL;

	cudaError_t cudaStatus = cudaSetDevice(0);
	if (cudaStatus != cudaSuccess) {
		fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");
		system("pause"); exit(1);
	}

	clock_t start_of_main = clock();
	
	////////////////////////////////////////////////////////////////////////////////////////
	//// UIview: User-Item rating matrix for training
	//// UIview_test: same dimension as UIview for testing score
	//// UUview: User-User friendship matrix which has all-ones diag and is symmetric
	////////////////////////////////////////////////////////////////////////////////////////

	UIview = (float*)malloc(nUsers*nItems*sizeof(float));
	CHECK_PARAM(UIview != NULL, "allocate memory space for UIview failed.");
	IUview = (float*)malloc(nItems*nUsers*sizeof(float));
	CHECK_PARAM(IUview != NULL, "allocate memory space for IUview failed.");
	UIview_test = (float*)malloc(nUsers*nItems*sizeof(float));
	CHECK_PARAM(UIview_test != NULL, "allocate memory space for UIview_test failed.");
	UUview = (float*)malloc(nUsers*nUsers*sizeof(float));
	CHECK_PARAM(UUview != NULL, "allocate memory space for UUview failed.");
	CHECK_PARAM(read_data_2_matrix(UIview, UIview_test, UUview, params.split_rate, IUview), "read data set failed.");

	ss.str("");
	ss << nUsers << "_" << nItems << "_" << "UIview_test.txt";
	write_test_data(UIview_test, nUsers, nItems, ss.str().c_str());
	free(UIview_test); UIview_test = NULL;

	clock_t end_of_read_dataset = clock();
	printf("read data set done. cost time:%.2f s\n", (double)(end_of_read_dataset - start_of_main) / CLOCKS_PER_SEC);
	start_of_main = clock();
	params.maxS = 5;
	params.minS = 1;
	params.num_of_kmeans = 3;
	params.num_of_LAE_iters = 10;
	params.num_of_anchors = 20;
	params.num_of_nearest_anchors = 3;

	std::string params_str;
	params_to_string(params, params_str);
	
	/////////////////////////////////////////////////////////
	//// 1. get anchors of uiview
	//// 2. get anchors of uuview
	//// 3. get trans-Posi matrix of users uZ
	/////////////////////////////////////////////////////////

	CHECK_CUDA(cudaMalloc(&d_UIview, nUsers*nItems*sizeof(float)));
	CHECK_CUDA(cudaMalloc(&d_anchors_of_uiview, params.num_of_anchors*nItems*sizeof(float)));
	CHECK_CUDA(cudaMemcpy(d_UIview, UIview, nUsers*nItems*sizeof(float), cudaMemcpyHostToDevice));

	get_anchors_by_Kmeans(d_UIview, d_anchors_of_uiview, nItems, nUsers, params.num_of_kmeans, params.num_of_anchors);

	anchors_of_uiview = (float*)malloc(params.num_of_anchors*nItems*sizeof(float));
	CHECK_PARAM(anchors_of_uiview != NULL, "allocate space for anchors of uiview failed.");
	CHECK_CUDA(cudaMemcpy(anchors_of_uiview, d_anchors_of_uiview, params.num_of_anchors*nItems*sizeof(float), cudaMemcpyDeviceToHost));
	
	clock_t end_of_kmeans1 = clock();
	printf("get uiview anchors cost time:%.2f s\n", (double)(end_of_kmeans1 - start_of_main) / CLOCKS_PER_SEC);
		
	CHECK_CUDA(cudaMalloc(&d_UUview, nUsers*nUsers*sizeof(float)));
	CHECK_CUDA(cudaMalloc(&d_anchors_of_uuview, params.num_of_anchors*nUsers*sizeof(float)));
	CHECK_CUDA(cudaMemcpy(d_UUview, UUview, nUsers*nUsers*sizeof(float), cudaMemcpyHostToDevice));

	get_anchors_by_Kmeans(d_UUview, d_anchors_of_uuview,
		nUsers, nUsers, params.num_of_kmeans, params.num_of_anchors);
		
	anchors_of_uuview = (float*)malloc(params.num_of_anchors*nUsers*sizeof(float));
	CHECK_PARAM(anchors_of_uuview != NULL, "anchors_of_uuview failed.");
	CHECK_CUDA(cudaMemcpy(anchors_of_uuview, d_anchors_of_uuview, params.num_of_anchors*nUsers*sizeof(float), cudaMemcpyDeviceToHost));
	clock_t end_of_kmeans2 = clock();
	printf("get uuview anchors cost time:%.2f s\n", (double)(end_of_kmeans2 - end_of_kmeans1) / CLOCKS_PER_SEC);
		
	uZ = (float*)malloc(nUsers * 2 * params.num_of_anchors*sizeof(float));
	CHECK_PARAM(uZ != NULL, "allocate memory space for uZ failed.\n");
	CHECK_CUDA_STATUS(cublasCreate(&cublashandle));
	get_uZ_by_LAE(UIview, UUview,
		d_UIview, d_UUview,
		anchors_of_uiview, anchors_of_uuview,
		d_anchors_of_uiview, d_anchors_of_uuview,
		nUsers, nItems, params.num_of_anchors,
		uZ,
		params.num_of_nearest_anchors,
		params.num_of_LAE_iters,
		cublashandle);
	CHECK_CUDA_STATUS(cublasDestroy(cublashandle));
	CHECK_CUDA(cudaFree(d_anchors_of_uiview)); d_anchors_of_uiview = NULL;
	CHECK_CUDA(cudaFree(d_anchors_of_uuview)); d_anchors_of_uuview = NULL;
	CHECK_CUDA(cudaFree(d_UIview)); d_UIview = NULL;
	CHECK_CUDA(cudaFree(d_UUview)); d_UUview = NULL;
		
	free(anchors_of_uiview); anchors_of_uiview = NULL;
	free(anchors_of_uuview); anchors_of_uuview = NULL;
		
	ss.str("");
	ss << nUsers << "_" << nItems << "_" << params_str << "_" << "uZ.txt";
	write_Z_into_file(uZ, 2 * params.num_of_anchors, nUsers, ss.str().c_str());
	free(uZ); uZ = NULL;
	clock_t end_of_uz = clock();
	printf("get uZ cost time:%.2f s\n", (double)(end_of_uz - end_of_kmeans2) / CLOCKS_PER_SEC);


	///////////////////////////////////////////////////////
	//// 1. get anchors of iuview
	//// 2. get trans-Posi matrix of items iZ
	///////////////////////////////////////////////////////

	CHECK_CUDA(cudaMalloc(&d_IUview, nItems*nUsers*sizeof(float)));
	CHECK_CUDA(cudaMemcpy(d_IUview, IUview, nItems*nUsers*sizeof(float), cudaMemcpyHostToDevice));
	CHECK_CUDA(cudaMalloc(&d_anchors_of_iuview, params.num_of_anchors*nUsers*sizeof(float)));

	get_anchors_by_Kmeans(d_IUview, d_anchors_of_iuview, nUsers, nItems, params.num_of_kmeans, params.num_of_anchors);

	anchors_of_iuview = (float*)malloc(params.num_of_anchors*nUsers*sizeof(float));
	CHECK_PARAM(anchors_of_iuview != NULL, "allocate space for anchors of iuview failed.");

	CHECK_CUDA(cudaMemcpy(anchors_of_iuview, d_anchors_of_iuview, params.num_of_anchors*nUsers*sizeof(float), cudaMemcpyDeviceToHost));
	clock_t end_of_kmeans3 = clock();
	printf("get iuview anchors cost time:%.2f s\n", (double)(end_of_kmeans3 - end_of_uz) / CLOCKS_PER_SEC);

	iZ = (float*)malloc(nItems * params.num_of_anchors*sizeof(float));
	CHECK_PARAM(iZ != NULL, "allocate memory space for iZ failed.");
	CHECK_CUDA_STATUS(cublasCreate(&cublashandle));
	get_iZ_by_LAE(IUview, d_IUview,
		anchors_of_iuview, d_anchors_of_iuview,
		nUsers, nItems, params.num_of_anchors,
		iZ,
		params.num_of_nearest_anchors,
		params.num_of_LAE_iters,
		cublashandle);
	CHECK_CUDA_STATUS(cublasDestroy(cublashandle));
	CHECK_CUDA(cudaFree(d_IUview)); d_IUview = NULL;
	CHECK_CUDA(cudaFree(d_anchors_of_iuview)); d_anchors_of_iuview = NULL;
	free(anchors_of_iuview); anchors_of_iuview = NULL;

	ss.str("");
	ss << nUsers << "_" << nItems << "_" << params_str << "_" << "iZ.txt";
	write_Z_into_file(iZ, params.num_of_anchors, nItems, ss.str().c_str());
	free(iZ); iZ = NULL;
	clock_t end_of_iz = clock();
	printf("get iZ cost time:%.2f s\n", (double)(end_of_iz - end_of_kmeans3) / CLOCKS_PER_SEC);
	
	

	float *d_uZ;
	int n = nUsers;
	int ud = 2 * params.num_of_anchors;
	CHECK_CUDA(cudaMalloc(&d_uZ, n*ud*sizeof(float)));

	//uZ = (float*)malloc(n * ud * sizeof(float));
	//CHECK_PARAM(uZ != NULL, "allocate memory space for uZ failed.");
	//ss.str("");
	//ss << nUsers << "_" << nItems << "_" << params_str << "_" << "uZ.txt";
	//read_Z_from_file(uZ, ud, n, ss.str().c_str());
	//CHECK_CUDA(cudaMemcpy(d_uZ, uZ, n*ud*sizeof(float), cudaMemcpyHostToDevice));
	//free(uZ);

	float *d_ulambda;
	CHECK_CUDA(cudaMalloc(&d_ulambda, ud*sizeof(float)));
	float *d_uM;
	CHECK_CUDA(cudaMalloc(&d_uM, ud*ud*sizeof(float)));

	CHECK_CUDA_STATUS(cublasCreate(&cublashandle));
	get_M_from_Z(d_uM, d_ulambda, d_uZ, n, ud, cublashandle);
	CHECK_CUDA_STATUS(cublasDestroy(cublashandle));

	float *d_uEigvals;
	CHECK_CUDA(cudaMalloc(&d_uEigvals, ud*sizeof(float)));
	solve_eig(d_uM, d_uEigvals, ud, ud);

	float *d_iZ;
	int m = nItems;
	int id = params.num_of_anchors;
	CHECK_CUDA(cudaMalloc(&d_iZ, m*id*sizeof(float)));
	
	//iZ = (float*)malloc(m * id*sizeof(float));
	//CHECK_PARAM(iZ != NULL, "allocate memory space for iZ failed.");
	//ss.str("");
	//ss << nUsers << "_" << nItems << "_" << params_str << "_" << "iZ.txt";
	//read_Z_from_file(iZ, id, m, ss.str().c_str());
	//CHECK_CUDA(cudaMemcpy(d_iZ, iZ, m*id*sizeof(float), cudaMemcpyHostToDevice));
	//free(iZ);

	float *d_ilambda;
	CHECK_CUDA(cudaMalloc(&d_ilambda, id*sizeof(float)));
	float *d_iM;
	CHECK_CUDA(cudaMalloc(&d_iM, id*id*sizeof(float)));
	CHECK_CUDA_STATUS(cublasCreate(&cublashandle));
	get_M_from_Z(d_iM, d_ilambda, d_iZ, m, id, cublashandle);
	CHECK_CUDA_STATUS(cublasDestroy(cublashandle));

	float *d_iEigvals;
	CHECK_CUDA(cudaMalloc(&d_iEigvals, id*sizeof(float)));
	solve_eig(d_iM, d_iEigvals, id, id);

	clock_t before_loop = clock();
	printf("before loop cost time : %.2f s\n", (double)(before_loop - end_of_read_dataset) / CLOCKS_PER_SEC);

	int x[5] = { 8, 16, 32, 64, 128 };
	for (int round = 0; round < 5; round++){
		clock_t start_of_round = clock();

		printf("===================================\nround:%d %d bits\n", round, x[round]);
		params.r = x[round];
		int r = params.r;

		float *B = (float *)malloc(params.r*nUsers*sizeof(float));
		CHECK_PARAM(B != NULL, "allocate space for B failed.");
		float *D = (float *)malloc(params.r*nItems*sizeof(float));
		CHECK_PARAM(D != NULL, "allocate space for D failed.");
		
		get_BinaryCode(B, d_uM, d_uEigvals, d_ulambda, d_uZ, r, ud, n);// overflow when calculating B code
		get_BinaryCode(D, d_iM, d_iEigvals, d_ilambda, d_iZ, r, id, m);// while D is OK
		
		ss.str("");
		ss << nUsers << "_" << nItems << "_" << params.r << "_" << params_str << "_" << "B_code.txt";
		write_into_file(B, params.r, nUsers, ss.str().c_str());
		ss.str("");
		ss << nUsers << "_" << nItems << "_" << params.r << "_" << params_str << "_" << "D_code.txt";
		write_into_file(D, params.r, nItems, ss.str().c_str());

		printf("write B and D into files done.\n");
		free(B); B = NULL;
		free(D); D = NULL;
		clock_t end_of_round = clock();
		printf("iter cost time %.2f s\n", (double)(end_of_round - start_of_round) / CLOCKS_PER_SEC);

	}
	clock_t end_of_main = clock();
	printf("total cost time %.2f s\n", (double)(end_of_main - start_of_main) / CLOCKS_PER_SEC);
	CHECK_CUDA(cudaFree(d_uM));
	CHECK_CUDA(cudaFree(d_uEigvals));
	CHECK_CUDA(cudaFree(d_ulambda));
	CHECK_CUDA(cudaFree(d_uZ));
	CHECK_CUDA(cudaFree(d_iZ));
	CHECK_CUDA(cudaFree(d_iM));
	CHECK_CUDA(cudaFree(d_iEigvals));
	CHECK_CUDA(cudaFree(d_ilambda));


	CHECK_CUDA(cudaDeviceReset());
	free(UUview); UUview = NULL;
	free(UIview); UIview = NULL;
	free(IUview); IUview = NULL;
	system("pause");
	return EXIT_SUCCESS; 
}